package com.auth.config.security;

import com.auth.config.details.UserDetailsServiceImpl;
import com.auth.config.filter.HttpAuthenticationEntryPoint;
import com.auth.config.filter.provider.PhoneAuthenticationProvider;
import com.auth.config.filter.provider.UsernameAuthenticationProvider;
import com.auth.config.handler.AuthFailureHandler;
import com.auth.config.handler.AuthSuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;
import java.util.Collections;


/**
 * 该配置类，主要处理用户名和密码的校验等事宜
 * @author HeWei123
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UsernameAuthenticationProvider usernameAuthenticationProvider;

    @Autowired
    private PhoneAuthenticationProvider phoneAuthenticationProvider;


    @Autowired
    private AuthenticationManager authenticationManager;

    @Value("${unifiedEntryUrl}")
    private String unifiedEntryUrl;

    /**
     * 注册一个认证管理器对象到容器
     *
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                .loginPage(unifiedEntryUrl)
                .loginProcessingUrl("/login")
                .successHandler(authenticationSuccessHandler())
                .failureHandler(authenticationFailureHandler())
                .and()
                .authorizeRequests()
                .antMatchers("/api/common/captcha","/api/oauth2.0/token","/demo-login","/oauth/authorize").permitAll()
                .anyRequest().authenticated()
                .and()
                .csrf().disable();
        http.addFilterBefore(httpAuthenticationEntryPoint(), UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    public HttpAuthenticationEntryPoint httpAuthenticationEntryPoint(){
        HttpAuthenticationEntryPoint httpAuthenticationEntryPoint = new HttpAuthenticationEntryPoint();
        httpAuthenticationEntryPoint.setAuthenticationFailureHandler(authenticationFailureHandler());
        httpAuthenticationEntryPoint.setAuthenticationSuccessHandler(authenticationSuccessHandler());
        httpAuthenticationEntryPoint.setAuthenticationManager(authenticationManager);
        return httpAuthenticationEntryPoint;
    }

    @Bean
    public AuthenticationFailureHandler authenticationFailureHandler(){
        return new AuthFailureHandler();
    }

    @Bean
    public AuthenticationSuccessHandler authenticationSuccessHandler(){
        return new AuthSuccessHandler();
    }


    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        // 配置那些域可以跨站请求资源
        configuration.setAllowedHeaders(Collections.singletonList("*"));
        configuration.setAllowedOrigins(Collections.singletonList("*"));
        // 配置支持的方法
        configuration.setAllowedMethods(Arrays.asList("GET","POST","PUT","OPTIONS"));
        configuration.setAllowCredentials(false);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/style/**");
    }

    /**
     * 处理用户名和密码验证事宜
     * 1)客户端传递username和password参数到认证服务器
     * 2)一般来说，username和password会存储在数据库中的用户表中
     * 3)根据用户表中数据，验证当前传递过来的用户信息的合法性
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 在这个方法中就可以去关联数据库了，当前我们先把用户信息配置在内存中
        // 实例化一个用户对象(相当于数据表中的一条用户记录)
        auth.authenticationProvider(usernameAuthenticationProvider)
                .authenticationProvider(phoneAuthenticationProvider)
                .userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }

    /**
     * userDetailsService()方法与单个用户一起建立内存用户存储。该用户的用户名为user，密码为password，角色为USER。
     *
     * @return
     */
    @Override
    @Bean("userDetailsService")
    public UserDetailsService userDetailsService() {
        return new UserDetailsServiceImpl();
    }
}
